package com.yxw.serialportutil;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import java.util.TooManyListenersException;

import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JTextArea;

import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

public class ReadSerialPort implements Runnable, SerialPortEventListener {

    private String appName = "串口调试助手";
    private int timeout = 2000;//open 端口时的等待时间
    private int threadTime = 0;
    private String sport;
    private CommPortIdentifier commPort;
    private SerialPort serialPort;
    private InputStream inputStream;
    private OutputStream outputStream;
    private JLabel sendAccountLable; 
	private JLabel reciveAccountLable;
    
    private JTextArea jta_rm;     //信息接收区域
	private JTextArea jta_sma;   //信息发送区域
    //JCheckBox ASCIICheckbox,JCheckBox checkDataCheckbox,JCheckBox timeCheckbox,JCheckBox ClearCheckbox,
	//JCheckBox addLineEndCheckbox,JCheckBox showSendCheckbox,
	//JComboBox<String> JComboBox_sendDataType
    private boolean isASCII;
    private String jinzhi;
    private boolean isChangeEnd;
    
    private JCheckBox ASCIICheckbox;
    private JCheckBox checkDataCheckbox;
    private JCheckBox timeCheckbox;
    private JCheckBox addLineEndCheckbox;
    private JComboBox<String> JComboBox_sendDataType;
    
    
    public JLabel getSendAccountLable() {
		return sendAccountLable;
	}

	public void setSendAccountLable(JLabel sendAccountLable) {
		this.sendAccountLable = sendAccountLable;
	}

	public JLabel getReciveAccountLable() {
		return reciveAccountLable;
	}

	public void setReciveAccountLable(JLabel reciveAccountLable) {
		this.reciveAccountLable = reciveAccountLable;
	}

	public JComboBox<String> getJComboBox_sendDataType() {
		return JComboBox_sendDataType;
	}

	public void setJComboBox_sendDataType(JComboBox<String> jComboBox_sendDataType) {
		JComboBox_sendDataType = jComboBox_sendDataType;
	}

	public JCheckBox getASCIICheckbox() {
		return ASCIICheckbox;
	}

	public void setASCIICheckbox(JCheckBox aSCIICheckbox) {
		ASCIICheckbox = aSCIICheckbox;
	}

	public JCheckBox getCheckDataCheckbox() {
		return checkDataCheckbox;
	}

	public void setCheckDataCheckbox(JCheckBox checkDataCheckbox) {
		this.checkDataCheckbox = checkDataCheckbox;
	}

	public JCheckBox getTimeCheckbox() {
		return timeCheckbox;
	}

	public void setTimeCheckbox(JCheckBox timeCheckbox) {
		this.timeCheckbox = timeCheckbox;
	}

	public JCheckBox getAddLineEndCheckbox() {
		return addLineEndCheckbox;
	}

	public void setAddLineEndCheckbox(JCheckBox addLineEndCheckbox) {
		this.addLineEndCheckbox = addLineEndCheckbox;
	}

	public boolean isChangeEnd() {
		return isChangeEnd;
	}

	public void setChangeEnd(boolean isChangeEnd) {
		this.isChangeEnd = isChangeEnd;
	}

	public boolean isASCII() {
		return isASCII;
	}

	public void setASCII(boolean isASCII) {
		this.isASCII = isASCII;
	}

	public String getJinzhi() {
		return jinzhi;
	}

	public void setJinzhi(String jinzhi) {
		this.jinzhi = jinzhi;
	}

	public JTextArea getJta_rm() {
		return jta_rm;
	}

	public void setJta_rm(JTextArea jta_rm) {
		this.jta_rm = jta_rm;
	}

	public JTextArea getJta_sma() {
		return jta_sma;
	}

	public void setJta_sma(JTextArea jta_sma) {
		this.jta_sma = jta_sma;
	}

	public CommPortIdentifier getCommPort() {
		return commPort;
	}

	public void setCommPort(CommPortIdentifier commPort) {
		this.commPort = commPort;
	}

	public SerialPort getSerialPort() {
		return serialPort;
	}

	public void setSerialPort(SerialPort serialPort) {
		this.serialPort = serialPort;
	}

	/**
     * @方法名称 :listPort
     * @功能描述 :列出所有可用的串口
     * @返回值类型 :void
     */
    @SuppressWarnings("rawtypes")
    public void listPort(){
        CommPortIdentifier cpid;
        Enumeration en = CommPortIdentifier.getPortIdentifiers();
        
        System.out.println("now to list all Port of this PC：" +en);
        
        while(en.hasMoreElements()){
            cpid = (CommPortIdentifier)en.nextElement();
            if(cpid.getPortType() == CommPortIdentifier.PORT_SERIAL){
                System.out.println(cpid.getName() + ", " + cpid.getCurrentOwner());
            }
        }
    }
    
    public ReadSerialPort() {
    }

    
    /**
     * @方法名称 :selectPort
     * @功能描述 :选择一个端口，比如：COM1
     * @返回值类型 :void
     *    @param portName
     */
    @SuppressWarnings("rawtypes")
    public void selectPort(String portName){
        
        this.commPort = null;
        CommPortIdentifier cpid;
        Enumeration en = CommPortIdentifier.getPortIdentifiers();
        
        while(en.hasMoreElements()){
            cpid = (CommPortIdentifier)en.nextElement();
            if(cpid.getPortType() == CommPortIdentifier.PORT_SERIAL
                    && cpid.getName().equals(portName)){
                this.commPort = cpid;
                System.out.println(commPort.getName());
                break;
            }
        }
        
        openPort();
    }
    
    /**
     * @方法名称 :openPort
     * @功能描述 :打开SerialPort
     * @返回值类型 :void
     */
    public void openPort(){
        if(commPort == null){
        	jta_rm.append("无法找到名字为"+commPort.getName()+"的串口！");
            log(String.format("无法找到名字为'%1$s'的串口！", commPort.getName()));
        }else{
        	//jta_rm.append("端口选择成功，当前端口："+commPort.getName()+",现在实例化 SerialPort:");
            log("端口选择成功，当前端口："+commPort.getName()+",现在实例化 SerialPort:");
            
            try{
            	//jta_rm.append("实例 SerialPort 成功！");
                //serialPort = (SerialPort)commPort.open(appName, timeout);
                log("实例 SerialPort 成功！");
            }catch(Exception e){
            	//jta_rm.append("端口"+commPort.getName()+"正在使用中！");
                throw new RuntimeException(String.format("端口'%1$s'正在使用中！",commPort.getName()));
            }
        }
    }
    
    /**
     * @方法名称 :checkPort
     * @功能描述 :检查端口是否正确连接
     * @返回值类型 :void
     */
    private void checkPort(){
        if(commPort == null)
            throw new RuntimeException("没有选择端口，请使用 " +
                    "selectPort(String portName) 方法选择端口");
        
        if(serialPort == null){
            throw new RuntimeException("SerialPort 对象无效！");
        }
    }
    
    /**
     * @方法名称 :write
     * @功能描述 :向端口发送数据，请在调用此方法前 先选择端口，并确定SerialPort正常打开！
     * @返回值类型 :void
     *    @param message
     */
    public void write(String message) {
        checkPort();
        
        try{
            outputStream = new BufferedOutputStream(serialPort.getOutputStream());
        }catch(IOException e){
            throw new RuntimeException("获取端口的OutputStream出错："+e.getMessage());
        }
        //rsp.setSendAccountLable(sendAccountLable);
        sendAccountLable.setText(""+(Integer.valueOf(sendAccountLable.getText())+message.length()));
        try{
        	System.out.println("writemessage"+message);
            outputStream.write(message.getBytes());
            log("信息发送成功！");
        }catch(IOException e){
            throw new RuntimeException("向端口发送信息时出错："+e.getMessage());
        }finally{
            try{
                outputStream.close();
            }catch(Exception e){
            }
        }
    }
    
    /**
     * @方法名称 :startRead
     * @功能描述 :开始监听从端口中接收的数据
     * @返回值类型 :void
     *    @param time  监听程序的存活时间，单位为秒，0 则是一直监听
     */
    public void startRead(int time){
        checkPort();
        
        try{
            inputStream = new BufferedInputStream(serialPort.getInputStream());
        }catch(IOException e){
            throw new RuntimeException("获取端口的InputStream出错："+e.getMessage());
        }
        
        try{
            serialPort.addEventListener(this);
        }catch(TooManyListenersException e){
            throw new RuntimeException(e.getMessage());
        }
        
        serialPort.notifyOnDataAvailable(true);
        //jta_rm.append("开始监听数据--------------");
        log(String.format("开始监听数据--------------"));
        if(time > 0){
            this.threadTime = time*1000;
            Thread t = new Thread(this);
            t.start();
            log(String.format("监听程序将在%1$d秒后关闭。。。。", threadTime));
        }
    }
    public void startRead(){
        checkPort();
        System.out.println(serialPort.getBaudRate());
        System.out.println(serialPort.getDataBits());
        System.out.println(serialPort.getStopBits());
        System.out.println(serialPort.getParity());
        System.out.println(serialPort.getName());
        try{
            inputStream = new BufferedInputStream(serialPort.getInputStream());
            
            byte[] bytes = new byte[0];
            bytes = new byte[inputStream.available()];
            inputStream.read(bytes);
            String str = new String(bytes);
            System.out.println(str);
            jta_rm.append(str);
        }catch(IOException e){
            throw new RuntimeException("获取端口的InputStream出错："+e.getMessage());
        }
        
        try{
            serialPort.addEventListener(this);
        }catch(TooManyListenersException e){
            throw new RuntimeException(e.getMessage());
        }
        
        serialPort.notifyOnDataAvailable(true);
        jta_rm.append("开始监听数据--------------");
        log(String.format("开始监听数据--------------"));
        
    }
    
    /**
     * @方法名称 :close
     * @功能描述 :关闭 SerialPort
     * @返回值类型 :void
     */
    public void close(){
        serialPort.close();
        serialPort = null;
        commPort = null;
    }
    
    
    public void log(String msg){
        System.out.println(appName+" --> "+msg);
    }
    

    /**
     * 数据接收的监听处理函数
     */
    @Override
    public void serialEvent(SerialPortEvent arg0) {
        switch(arg0.getEventType()){
        case SerialPortEvent.BI:/*Break interrupt,通讯中断*/ 
        case SerialPortEvent.OE:/*Overrun error，溢位错误*/ 
        case SerialPortEvent.FE:/*Framing error，传帧错误*/
        case SerialPortEvent.PE:/*Parity error，校验错误*/
        case SerialPortEvent.CD:/*Carrier detect，载波检测*/
        case SerialPortEvent.CTS:/*Clear to send，清除发送*/ 
        case SerialPortEvent.DSR:/*Data set ready，数据设备就绪*/ 
        case SerialPortEvent.RI:/*Ring indicator，响铃指示*/
        case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/*Output buffer is empty，输出缓冲区清空*/ 
            break;
        case SerialPortEvent.DATA_AVAILABLE:/*Data available at the serial port，端口有可用数据。读到缓冲数组，输出到终端*/
            byte[] readBuffer = new byte[1024];
            String readStr="";
            String s2 = "";
            try {
            	int availableBytes = 0;
            	
            	availableBytes = inputStream.available();
                while (availableBytes > 0) {
                    inputStream.read(readBuffer);        
                    for(int j = 0;j < readBuffer.length && j < availableBytes;j++){
                        //因为COM11口发送的是使用byte数组表示的字符串，
                        //所以在此将接收到的每个字节的数据都强制装换为char对象即可，
                    	//System.out.print("  String" + String.valueOf(readBuffer[j]));
                        //System.out.print("  char" + (char)readBuffer[j]);
                        readStr += new String(String.valueOf(readBuffer[j])).trim();
                    }
                    //String str= new String (readBuffer);
                    //System.out.println("str"+str);
                    readBuffer = null;
                    availableBytes = inputStream.available();
                }
                System.out.println("原始信息："+readStr);
                if(!ASCIICheckbox.isSelected()){
                	switch (JComboBox_sendDataType.getSelectedItem().toString()){
						case "二进制":readStr = toBinary(readStr);break;
						case "八进制":readStr = toOctal(readStr);break;
						case "十六进制":readStr = hex2byte(readStr);break;
                	}
                }
                
                //readStr += new String(readBuffer).trim();
                //s2 = new String(readBuffer).trim();
                System.out.println("s2:" + s2);
                //rsp.setReciveAccountLable(reciveAccountLable);
				//rsp.setSendAccountLable(sendAccountLable);
                reciveAccountLable.setText(""+(Integer.valueOf(reciveAccountLable.getText())+readStr.length()));
                if(timeCheckbox.isSelected()){
					SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
					readStr = "[" + df.format(new Date()) + "]" + readStr;
				}
                
                if(addLineEndCheckbox.isSelected()){
                	//jta_rm.append("\n");
                	readStr = "\n" + readStr;
                }
                
                jta_rm.append(readStr);
                
                log("接收到端口返回数据(长度为"+readStr.length()+")："+readStr);
                log(s2);

                
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //16进制转换
    private String hex2byte(String hex) {

        String digital = "0123456789ABCDEF";
        String hex1 = hex.replace(" ", "");
        char[] hex2char = hex1.toCharArray();
        byte[] bytes = new byte[hex1.length() / 2];
        byte temp;
        for (int p = 0; p < bytes.length; p++) {
            temp = (byte) (digital.indexOf(hex2char[2 * p]) * 16);
            temp += digital.indexOf(hex2char[2 * p + 1]);
            bytes[p] = (byte) (temp & 0xff);
        }
        String s = "";
        for(int j = 0;j < bytes.length;j++){
            s += new String(String.valueOf(bytes[j])).trim();
        }
        
        return s;
    }
    
    //二进制转换
    public String toBinary(String str){
        char[] strChar=str.toCharArray();
        String result="";
        for(int i=0;i<strChar.length;i++){
            result +=Integer.toBinaryString(strChar[i] - '0')+ " ";
        }
        System.out.println(result);
        return result;
    }
    
    //8进制转换
    public String toOctal(String str){
	    //String bin = toBinary(str);
	    String result="";
	    char[] strChar=str.toCharArray();
	    
	    for(int i=0;i<strChar.length;i++){
	        result +=Integer.toOctalString(strChar[i] - '0')+ " ";
	    }
	    System.out.println(result);
	    return result;
    }

    public long getTime(){
    	System.currentTimeMillis();
    	Calendar.getInstance().getTimeInMillis();
    	return new Date().getTime();
    }

    @Override
    public void run() {
        try{
            Thread.sleep(threadTime);
            serialPort.close();
            log(String.format("端口''监听关闭了！", commPort.getName()));
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    public String getSport() {
        return sport;
    }

    public void setSport(String sport) {
        this.sport = sport;
    }
}